/*
 *  Copyright (C) 2004 Cidero, Inc.
 *
 *  Permission is hereby granted to any person obtaining a copy of 
 *  this software to use, copy, modify, merge, publish, and distribute
 *  the software for any non-commercial purpose, subject to the
 *  following conditions:
 *  
 *  The above copyright notice and this permission notice shall be included
 *  in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY IN CONNECTION WITH THE SOFTWARE.
 * 
 *  File: $RCSfile: TwonkyPlaylistConverter.java,v $
 *
 */
package com.cidero.util;

import java.io.*;
import java.util.ArrayList;
import java.util.logging.Logger;

import com.cidero.util.URLUtil;
import com.cidero.upnp.*;


/**
 * Twonky playlist converter class. Twonky keeps a database on disk with
 * all the 'real' filename info (unlike the virtual filenames exported
 * by the server).  This code exists to allow the real filenames for 
 * a given UPnP object to be determined, so a real M3U or PLS playlist
 * can be created for a list of UPnP objects. 
 *
 * Twonky DB format is a simple ASCII format that looks like the following:
 *
 *  <SOI>
 *  TI:Fly Away
 *  CL:object.item.audioItem.musicTrack
 *  AR:Poe
 *  AL:Hello
 *  GE:Rock/pop
 *  RE:N:\media\music\Poe\Hello\11.mp3
 *  DU:0:04:34.656
 *  TR:11
 *  AA:37
 *  SI:6593810
 *  MT:1103939278
 *  <EOI>
 *  <SOI>
 *  TI:Junkie
 *  CL:object.item.audioItem.musicTrack
 *  AR:Poe
 *  AL:Hello
 *  GE:Rock/pop
 *  RE:N:\media\music\Poe\Hello\10.mp3
 *  DU:0:03:00.459
 *  TR:10
 *  AA:37
 *  SI:4333069
 *  MT:1103939221
 *  <EOI>
 *  
 */
public class TwonkyPlaylistConverter
{
  private static Logger logger = Logger.getLogger("com.cidero.util");

  String dbFilename;
  
  /**
   *  Constructor
   */ 
  public TwonkyPlaylistConverter( String dbFilename )
  {
    this.dbFilename = dbFilename;
  }

  public void objListToPlaylist( CDSObjectList objList,
                                 String playlistFilename )
    throws IOException
  {
    objListToPlaylist( objList, new java.io.File( playlistFilename) );
  }
  

  /**
   *
   */
  public void objListToPlaylist( CDSObjectList objList,
                                 java.io.File playlistFile )
    throws IOException
  {
    FileInputStream fileInputStream = new FileInputStream( dbFilename );
    
    Reader reader = new InputStreamReader( fileInputStream );

    BufferedReader bufReader = new BufferedReader( reader );

    // Number of objects in list is most likely fewer than in entire 
    // database, so set it up to make single pass through db
    CDSMusicTrack twonkyTrack = null;
    
    CDSObjectList clonedObjList = new CDSObjectList();
    for( int n = 0 ; n < objList.size() ; n++ )
    {
      CDSObject obj = objList.getObject(n);
      if( ! (obj instanceof CDSMusicTrack) )
        continue;

      CDSObject clonedObj = (CDSObject)obj.clone();
      clonedObj.clearResources();
      clonedObjList.add( clonedObj );
    }
    
    while( (twonkyTrack = 
            getTwonkyDbMusicTrack(bufReader)) != null )
    {
      for( int n = 0 ; n < clonedObjList.size() ; n++ )
      {
        CDSMusicTrack track = (CDSMusicTrack)clonedObjList.getObject(n);

        if( track.getArtist().equals( twonkyTrack.getArtist() ) &&
            track.getAlbum().equals( twonkyTrack.getAlbum() ) &&
            track.getTitle().equals( twonkyTrack.getTitle() ) )
        {
          // Match - store resource in cloned obj 
          track.addResource( twonkyTrack.getResource(0) );
        }
      }
    }

    bufReader.close();
    
    
    // Now write out the playlist as an M3U playlist

    FileOutputStream fileOutputStream = new FileOutputStream( playlistFile );

    PrintWriter writer = new PrintWriter(
                   new BufferedOutputStream( fileOutputStream ) );

    for( int n = 0 ; n < clonedObjList.size() ; n++ )
    {
      CDSMusicTrack track = (CDSMusicTrack)clonedObjList.getObject(n);
      if( track.getResourceCount() < 1 )
      {
        logger.warning("No match found in Twonky Db for track");
        logger.warning("Artist: " + track.getArtist() +
                       "Album:" + track.getAlbum() +
                       "Title:" + track.getTitle() );
        continue;
      }
      
      writer.println( track.getResource(0).getName() );
    }

    writer.flush();
    writer.close();
  }

  public CDSMusicTrack getTwonkyDbMusicTrack( BufferedReader reader )
    throws IOException
  {
    String artist;
    String album;
    String title;
    String resourceName;

    String line;
    
    while( (line = getNonBlankLine( reader )) != null )
    {
      // Sync to start of record
      while( (line != null) && (! line.startsWith("<SOI>")) )
      {
        logger.info("Not sync'd - skipping line: " + line );
        line = getNonBlankLine( reader );
      }
    
      // Read record - ignore objects that aren't music tracks

      boolean foundMusicTrack = false;
      CDSMusicTrack track = new CDSMusicTrack();
      CDSResource resource = new CDSResource();
      track.addResource( resource );

      while( ((line = getNonBlankLine( reader )) != null) &&
             (! line.startsWith("<EOI>")) )
      {
        int firstColonIndex = line.indexOf(":");
        
        String name = line.substring(0,firstColonIndex).trim();
        String value = line.substring(firstColonIndex+1).trim();

        if( name.equals("TI") )
          track.setTitle( value );
        else if( name.equals("AR") )
          track.setArtist( value );
        else if( name.equals("AL") )
          track.setAlbum( value );
        else if( name.equals("RE") )
          resource.setName( value );
        else if( name.equals("SI") )
          resource.setSize( Integer.parseInt(value) );
        else if( name.equals("CL") && (line.indexOf("musicTrack") > 0) ) 
          foundMusicTrack = true;
      }
      
      if( foundMusicTrack )
      {
        return track;
      }
    }

    return null;
  }

  public String getNonBlankLine( BufferedReader reader )
    throws IOException
  {
    String line;
    while( (line = reader.readLine()) != null )
    {
      //System.out.println( "NONBLANK Line: " + line );
      
      String trimmedLine = line.trim();
      if( trimmedLine.length() > 0 )
        return trimmedLine;
    }
    return null;
  }

  /**
   *  Standalone version of program that converts a list of objects in 
   *  DIDL-Lite format to a simple playlist, using the Twonky
   *  database to grab the 'real' diskfilenames
   */
  public static void usage()
  {
    System.out.println("Usage: TwonkyPlaylistConverter <XML playlist> <twonkyDb> <M3U playlist>" );
    System.exit(-1);
  }
    
  public static void main( String[] args )
  {
    if( args.length < 3 )
      usage();
    
    String xmlPlaylist = args[0];
    String twonkyDbFile = args[1];
    String m3uPlaylist = args[2];
    
    try 
    {
      BufferedInputStream xmlInputStream =
      new BufferedInputStream( new FileInputStream( xmlPlaylist ) );
                                                                 
      CDSObjectList objList = new CDSObjectList( xmlInputStream );

      System.out.println("Read " + objList.size() + " tracks from XML file" );

      TwonkyPlaylistConverter converter = 
        new TwonkyPlaylistConverter( twonkyDbFile );
    
      System.out.println("Converted tracks to M3U file " + m3uPlaylist );

      converter.objListToPlaylist( objList, m3uPlaylist );
    
      System.out.println("Done ");

    }
    catch( Exception e )
    {
      e.printStackTrace();
    }
  }
}
